package org.goldenport.g3.adapters

import scala.xml.{XML, Elem}
import java.io._
import java.net.URL
import org.goldenport.wpath.WPathUtil.concat
import org.goldenport.g3._
import org.goldenport.g3.messages._
import org.goldenport.g3.messages.file._
import org.goldenport.g3.events.file._
import com.asamioffice.goldenport.io.{UFile, UURL, UIO}
import org.goldenport.entity.GEntity
import org.goldenport.entity.content.GContent
import org.goldenport.entity.datasource.GDataSource

// XXX use google commons

/**
 * @since   Apr. 14, 2010
 * @version Oct. 10, 2010
 * @author  ASAMI, Tomoharu
 */
class FileAdapter(val uri: String) extends G3Adapter with ActivityActionable {
  val driver = new FileDriver(uri)

  def create_Activity(ctx: G3AgentContext) = driver.createDriverActivity(ctx)

/*
  override protected def do_Action(content: AnyRef, parcel: G3Parcel, ctx: G3AgentContext): AnyRef = {
    content match {
      case cmd: G3Command => driver.execute(cmd, ctx)
      case blob: Blob => driver.execute(file.Create(blob), ctx)
      case string: String => driver.execute(file.Create(string), ctx)
      case gentity: GEntity => driver.execute(file.Create(gentity), ctx)
      case gcontent: GContent => driver.execute(file.Create(gcontent), ctx)
    }
  }
*/
/*
  override protected def do_Action(content: AnyRef, ctx: G3AgentContext): AnyRef = {
    content match {
      case clob: Clob => {
        create_file(clob.openInputStream())
        clob
      }
      case blob: Blob => {
        create_file(blob.openInputStream())
        blob
      }
      case xml: Elem => {
        XML.save(file.getAbsolutePath(), xml, encoding)
        xml
      }
      case text: String => {
        write_file(text)
        text
      }
      case create: filestore.Create => {
        create_file(create.openInputStream())
      }
      case read: filestore.Read => {
        read_file()
      }
      case write: filestore.Write => {
        write_file(write.openInputStream())
      }
      case remove: filestore.Remove => {
        remove_file()
      }
      case get: Get => {
        read_file()
      }
      case get: http.GET => {
        read_file()
      }
      case _ => {
        error("XXX FileAdapter")
      }
    }
  }

  private def create_file(in: InputStream) {
    write_file(in)
  }

  private def create_file(optionIn: Option[InputStream]) {
    optionIn match {
      case Some(in) => write_file(in)
      case None => UFile.createFile(file, "")
    }
  }

  private def write_file(in: InputStream) {
    try {
      UFile.createFile(file, in)
    } finally {
      in.close()
    }
  }

  private def write_file(string: String) {
    UFile.createFile(file, string, encoding)
  }

  private def read_file() = {
    if (isText) {
      val in = url.openStream()
      try {
        UIO.stream2String(in, encoding)
      } finally {
        in.close()
      }
    } else {
      val in = url.openStream()
      try {
        UIO.stream2Bytes(in)
      } finally {
        in.close()
      }
    }
  }

  private def remove_file() {
    file.delete()
  }
*/
}

class FileDriver(val uri: String, val encoding: String = "utf-8", val mime: Option[String] = None) extends G3Driver {
  override protected def driver_Activity(ctx: G3AgentContext): PartialFunction[AnyRef, AnyRef] = {
    case cmd: G3Command => execute(cmd, ctx) // XXX
    case blob: Blob => execute(file.Create(blob), ctx)
    case string: String => execute(file.Create(string), ctx)
    case gentity: GEntity => execute(file.Create(gentity), ctx)
    case gcontent: GContent => execute(file.Create(gcontent), ctx)
  }

  protected def new_Agent(cmd: G3Command, ctx: G3AgentContext): FileDriverAgent = {
    new FileDriverAgent(uri, encoding, mime, ctx)
  }
}

class FileDriverAgent(uri: String, val encoding: String, val mime: Option[String], context: G3AgentContext) extends G3DriverAgent(uri, context) {
  agent {
    case cmd: Create => create_file(cmd)
    case cmd: Read => read_file(cmd)
    case cmd: Write => write_file(cmd)
    case cmd: Remove => remove_file(cmd)
    case cmd: http.GET => read_file(cmd)
  }

  //
  def get(get: Get): G3Event = read_file(get)

  def post(post: Post): G3Event = {
    post.content match {
      case Some(blob: Blob) =>
        create_file(new file.BlobCreate(post.uri, blob))
      case Some(string: String) =>
        create_file(new file.StringCreate(post.uri, string))
      case Some(gentity: GEntity) =>
        create_file(new file.GEntityCreate(post.uri, gentity))
      case Some(gcontent: GContent) =>
        create_file(new file.GContentCreate(post.uri, gcontent))
      case _ => error("Unkown content: " + post)
    }
  }

  def put(put: Put): G3Event = {
    put.content match {
      case Some(blob: Blob) =>
        write_file(new file.BlobWrite(put.uri, blob))
      case Some(string: String) =>
        write_file(new file.StringWrite(put.uri, string))
      case Some(gentity: GEntity) =>
        write_file(new file.GEntityWrite(put.uri, gentity))
      case Some(gcontent: GContent) =>
        write_file(new file.GContentWrite(put.uri, gcontent))
      case _ => error("Unkown content: " + put)
    }
  }

  def delete(delete: Delete): G3Event = {
    error("XXX")
  }

  def mkcol(mkcol: Mkcol): G3Event = {
    error("XXX")
  }

  //
  private def create_file(cmd: Create): G3Event = {
//    println("create_file:" + cmd.uri)
    val ds = get_datasource(cmd.uri)
    ds.getFile match {
      case Some(file: File) => {
        cmd.openInputStream match {
          case Some(in) => write_file(cmd, in)
          case None => UFile.createFile(file, "")
        }
        new FileCreated(uri)
      }
      case None => error("create_file: " + ds)
    }
  }

  private def read_file(cmd: Read): G3Event = read_file(cmd)

  private def write_file(cmd: Write): G3Event = {
    write_file(cmd, cmd.openInputStream)
    new FileWrited(uri)
  }

  private def remove_file(cmd: Remove): G3Event = {
    val ds = get_datasource(cmd.uri)
    ds.getFile match {
      case Some(file: File) => {
        file.delete()
        new FileRemoved(uri)
      }
      case None => error("remove_file: " + ds)
     }
  }

  //
  private def read_file(cmd: Get): G3Event = {
    val ds = get_datasource(cmd.uri)
    ds.getUrl match {
      case Some(url: URL) => {
        val result = context.reconstitute(url)
        new FileReaded(uri, result)
      }
      case None => error("remove_file: " + ds)
    }
  }

  private def write_file(cmd: G3Command, in: InputStream) {
    val ds = get_datasource(cmd.uri)
    try {
      ds.getFile match {
        case Some(file: File) => {
          UFile.createFile(file, in)
        }
        case None => error("remove_file: " + ds)
      }
    } finally {
      in.close()
    }
  }

  private def get_datasource(cmduri: String): GDataSource = {
    context.getDataSource(concat(uri, cmduri))
  }
}
